<?php
/**
 * @link https://gitee.com/toshcn/yii2-oauth2-server
 * @copyright Copyright (c) 2018 len168.com
 * @license https://gitee.com/toshcn/yii2-oauth2-server/blob/master/LICENSE
 */

use yii\db\Migration;

/**
 * Class m180718_065257_oauth2_server
 */
class m180718_065257_oauth2_server extends Migration
{
    const OAUTH_CLIENTS = '{{%oauth_clients}}';
    const OAUTH_ACCESS_TOKENS = '{{%oauth_access_tokens}}';
    const OAUTH_REFRESH_TOKENS = '{{%oauth_refresh_tokens}}';
    const OAUTH_AUTHORIZATION_CODES = '{{%oauth_authorization_codes}}';
    const OAUTH_SCOPES = '{{%oauth_scopes}}';
    const OAUTH_JWT = '{{%oauth_jwt}}';
    const OAUTH_USERS = '{{%oauth_users}}';
    const OAUTH_PUBLIC_KEYS = '{{%oauth_public_keys}}';

    public function mysql($yes, $no='') {
        return $this->db->driverName === 'mysql' ? $yes : $no;
    }

    /**
     * {@inheritdoc}
     */
    public function up()
    {
        $tableOptions = null;
        if ($this->db->driverName === 'mysql') {
            // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci
            $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
        }
        $now          = $this->mysql('CURRENT_TIMESTAMP',"'now'");
        $onUpdateNow  = $this->mysql("ON UPDATE $now");

        $transaction = $this->db->beginTransaction();
        try {
            $this->createTable(static::OAUTH_CLIENTS, [
                'client_id' => $this->string(80)->unique()->notNull(),
                'client_secret' => $this->string(80)->notNull(),
                'redirect_uri' => $this->string(2000)->notNull(),
                'grant_types' => $this->string(100)->notNull(),
                'scope' => $this->string(2000)->null(),
                'user_id' => $this->integer()->unsigned()->notNull()->defaultValue(0),
            ], $tableOptions);
            $this->addPrimaryKey('pk_client_id', static::OAUTH_CLIENTS, 'client_id');

            $this->createTable(static::OAUTH_ACCESS_TOKENS, [
                'access_token' => $this->string(80)->unique()->notNull(),
                'client_id' => $this->string(80)->notNull(),
                'user_id' => $this->integer()->unsigned()->notNull(),
                'expires' => $this->timestamp()->notNull()->defaultExpression("{$now} {$onUpdateNow}"),
                'scope' => $this->string(2000)->null(),
            ], $tableOptions);
            $this->addPrimaryKey('pk_access_token', static::OAUTH_ACCESS_TOKENS, 'access_token');
            $this->addForeignKey('fk_client_id-access_token', static::OAUTH_ACCESS_TOKENS, 'client_id', static::OAUTH_CLIENTS, 'client_id', 'CASCADE', 'CASCADE');

            $this->createTable(static::OAUTH_REFRESH_TOKENS, [
                'refresh_token' => $this->string(80)->unique()->notNull(),
                'client_id' => $this->string(80)->notNull(),
                'user_id' => $this->integer()->unsigned()->notNull(),
                'expires' => $this->timestamp()->notNull()->defaultExpression("{$now} {$onUpdateNow}"),
                'scope' => $this->string(2000)->null(),
            ], $tableOptions);
            $this->addPrimaryKey('pk_refresh_token', static::OAUTH_REFRESH_TOKENS, 'refresh_token');
            $this->addForeignKey('fk_client_id-refresh_token', static::OAUTH_REFRESH_TOKENS, 'client_id', static::OAUTH_CLIENTS, 'client_id', 'CASCADE', 'CASCADE');

            $this->createTable(static::OAUTH_AUTHORIZATION_CODES, [
                'authorization_code' => $this->string(80)->unique()->notNull(),
                'client_id' => $this->string(80)->notNull(),
                'user_id' => $this->integer()->unsigned()->notNull(),
                'redirect_uri' => $this->string(2000)->notNull(),
                'expires' => $this->timestamp()->notNull()->defaultExpression("{$now} {$onUpdateNow}"),
                'scope' => $this->string(2000)->null(),
            ], $tableOptions);
            $this->addPrimaryKey('pk_authorization_code', static::OAUTH_AUTHORIZATION_CODES, 'authorization_code');
            $this->addForeignKey('fk_client_id-authorization_code', static::OAUTH_AUTHORIZATION_CODES, 'client_id', static::OAUTH_CLIENTS, 'client_id', 'CASCADE', 'CASCADE');

            $this->createTable(static::OAUTH_SCOPES, [
                'scope' => $this->string(2000)->notNull(),
                'is_default' => $this->boolean()->notNull(),
            ], $tableOptions);

            $this->createTable(static::OAUTH_JWT, [
                'client_id' => $this->string(80)->unique()->notNull(),
                'subject' => $this->string(80)->defaultValue(''),
                'public_key' => $this->string(2000)->defaultValue(''),
            ], $tableOptions);
            $this->addPrimaryKey('pk_client_id', static::OAUTH_JWT, 'client_id');

            $this->createTable(static::OAUTH_USERS, [
                'username' => $this->string(255)->unique()->notNull(),
                'password' => $this->string(2000)->defaultValue(''),
                'first_name' => $this->string(255)->defaultValue(''),
                'last_name' => $this->string(255)->defaultValue(''),
            ], $tableOptions);
            $this->addPrimaryKey('pk_username', static::OAUTH_USERS, 'username');

            $this->createTable(static::OAUTH_PUBLIC_KEYS, [
                'client_id' => $this->string(255)->unique()->notNull(),
                'public_key' => $this->string(2000)->defaultValue(''),
                'private_key' => $this->string(2000)->defaultValue(''),
                'encryption_algorithm' => $this->string(2000)->defaultValue('RS256'),
            ], $tableOptions);
            $this->addPrimaryKey('pk_client_id', static::OAUTH_PUBLIC_KEYS, 'client_id');

            // insert client data
            $this->batchInsert(static::OAUTH_CLIENTS, ['client_id', 'client_secret', 'redirect_uri', 'grant_types'], [
                ['testclient', 'testpass', 'http://example/', 'client_credentials authorization_code password implicit'],
            ]);
            $transaction->commit();
        } catch (Exception $e) {
            echo 'Exception: ' . $e->getMessage() . '\n';
            $transaction->rollback();

            return false;
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function down()
    {
        $transaction = $this->db->beginTransaction();
        try {
            $this->dropTable(static::OAUTH_PUBLIC_KEYS);
            $this->dropTable(static::OAUTH_USERS);
            $this->dropTable(static::OAUTH_JWT);
            $this->dropTable(static::OAUTH_SCOPES);
            $this->dropTable(static::OAUTH_AUTHORIZATION_CODES);
            $this->dropTable(static::OAUTH_REFRESH_TOKENS);
            $this->dropTable(static::OAUTH_ACCESS_TOKENS);
            $this->dropTable(static::OAUTH_CLIENTS);

            $transaction->commit();
        } catch (Exception $e) {
            $transaction->rollback();
            echo $e->getMessage();
            echo "\n";
            echo get_called_class() . ' cannot be reverted.';
            echo "\n";

            return false;
        }
        return true;
    }
}
